#pragma once

template<class T>
class CAllocator
{
	void	*	m_pAlloc;
	int			m_nNumAllocations;
	size_t		m_nSize;
	size_t		m_nMemoryUsed;
	size_t		m_nMaxSize;

	CAllocator(size_t size, size_t max_size)	:	m_pAlloc(new char[(size_t)AlignAddress(size)]),
													m_nSize((size_t)AlignAddress(size)),
													m_nMemoryUsed(0),
													m_nMaxSize(max_size)
	{
	}
	virtual ~CAllocator()
	{
		ASSERT(m_nNumAllocations == 0 && m_nMemoryUsed == 0);
		delete m_pAlloc;
	}

	void	*	GetMemory(size_t num_bytes)
	{
		num_bytes = (size_t)AlignAddress(num_bytes, 4);
		while(m_nSize < m_nMemoryUsed + num_bytes)
		{
			new ((char*)(AlignAddress((size_t)m_pAlloc + m_nSize, 4))) char[m_nSize * 2];
			m_nSize += m_nSize * 2;
		}
		void * mem = AlignAddress(((size_t)m_pAlloc + m_nMemoryUsed, 4));
		m_nMemoryUsed += num_bytes;
		return mem;
	}
};

class CHeap
{
	void	*	m_pStart;
	void	*	m_pCurrent;
	size_t		m_nEnd;

public:
	CHeap(size_t size)
	{
		m_pCurrent = m_pStart = malloc(size);
		m_nEnd = size + (size_t)m_pStart;
	}
	virtual ~CHeap()
	{
		free(m_pStart);
	}

	virtual void	*	Allocate(size_t size)
	{
		size_t pCurrent = ((size_t)m_pCurrent) + size;
		if(pCurrent > m_nEnd) throw(std::bad_alloc());
		void * pMem = m_pCurrent;
		m_pCurrent = (void*)pCurrent;
		return pMem;
	}
};

template<typename T>
class CPool
{
	T			*	m_pData;
	bool		*	m_pTaken;
	size_t			m_nNumTaken;
	size_t			m_pTop;
	size_t			m_nSize;

	inline size_t		GetSlot(T * obj)						{return m_pData - obj;}
	inline T		*	GetAtSlot(size_t nSlot)					{return &m_pData[nSlot];}
	inline bool			IsSlotFree(size_t nSlot)				{return !m_pTaken[nSlot];}

public:
	CPool(size_t size)		:	m_pData((T*)calloc(size, sizeof(T))),
								m_pTaken((bool*)calloc(size, sizeof(bool))),
								m_nNumTaken(0),
								m_pTop((size_t)m_pData),
								m_nSize(size)
	{
		if(m_pData == nullptr)
			throw(std::bad_alloc());
	}
	~CPool()
	{
		if(m_pData) free(m_pData);
	}

	T	*	Allocate()
	{
		for(size_t i=0; i<m_nSize; ++i)
		{
			if(IsSlotFree(i))
			{
				++m_nNumTaken;
				m_pTaken[i] = true;
				return GetAtSlot(i);
			}
		}
		return nullptr;
	}
	void	Deallocate(T * obj)
	{
		--m_nNumTaken;
		m_pTaken[GetSlot(obj)] = false;
	}
	inline bool	HasFreeSlots()					{return m_nSize > m_nNumTaken;}
	inline bool Contains(T *obj)				{return GetSlot(obj) > 0 && GetSlot(obj) < m_nSize;}
};